#!/usr/bin/env python
"""The Artillery program. Sets up the map and game based on command line options
and runs it.
"""
from __future__ import division

__author__ = "Ragzouken"
__copyright__ = "Copyright (C) 2008 Ragzouken"
__license__ = "New BSD License"
__version__ = "1.0"

from os.path import join
from optparse import OptionParser
_PARSER = OptionParser()
_PARSER.add_option("-m", "--map", help="Load map from heightmap MAP.", 
	default="sunken", dest="map", metavar="MAP")
_PARSER.add_option("-t", "--terrain", help="Use TERRAIN as terrain.", 
	dest="terrain", default=None, metavar="TERRAIN")
_PARSER.add_option("-s", "--scale", help="Scale map heights by SCALE.", 
	dest="scale", default=8, type="int", metavar="SCALE")
_PARSER.add_option("-q", "--tilesize", help="Seperate vertices by TILESIZE pixels.", 
	dest="tilesize", default=16, type="float", metavar="TILESIZE")
_PARSER.add_option("-p", "--profile", help="Profile Artillery.", 
	action="store_true", dest="profile", default=False)
OPTIONS, ARGS = _PARSER.parse_args()

import logging

FORMATTER = logging.Formatter('%(levelname)-8s %(message)s')

TERMINAL = logging.StreamHandler()
TERMINAL.setLevel(logging.WARNING)
TERMINAL.setFormatter(FORMATTER)

LOAD_LOGGER = logging.getLogger('artillery.loading')
LOAD_LOGGER.addHandler(TERMINAL)
LOAD_LOGGER.setLevel(logging.INFO)

import pygame
import OpenGL.GL as ogl 

from heightmap import HeightMap, read
from game import Game
import terrain
import lamina

pygame.init()
WINDOW = pygame.display.set_mode((640, 480), pygame.OPENGL | pygame.DOUBLEBUF)
pygame.display.set_caption('Artillery') 
SCREEN = pygame.display.get_surface()

FONT = pygame.font.Font("text.ttf", 8)
def text(string):
	"""Render a pygame font basically."""
	return FONT.render(string, False, (255, 255, 255), (0, 0, 0))

CLOCK = pygame.time.Clock()

def _test():
	"""Profile artillery roughly by loading a map repeatedly."""
	for _ in xrange(100):
		HeightMap(OPTIONS.map)

if OPTIONS.profile:
	import cProfile
	cProfile.run("_test()")

try:
	import psyco
	from psyco.classes import *
except ImportError:
	pass
else:
	psyco.full()

## OPENGL EVIL ###
ogl.glClearColor(0, 0, 0, 0)
ogl.glEnable(ogl.GL_DEPTH_TEST)
ogl.glEnable(ogl.GL_CULL_FACE)
ogl.glCullFace(ogl.GL_BACK)
ogl.glEnable(ogl.GL_NORMALIZE)

ogl.glEnable(ogl.GL_BLEND)

ogl.glHint(ogl.GL_LINE_SMOOTH_HINT, ogl.GL_NICEST)
ogl.glShadeModel(ogl.GL_FLAT)

#ogl.glEnableClientState(ogl.GL_NORMAL_ARRAY)
#ogl.glEnableClientState(ogl.GL_VERTEX_ARRAY)
#ogl.glEnableClientState(ogl.GL_COLOR_ARRAY)

PANEL = lamina.LaminaPanelSurface((-320, 240, 640, 480), (640, 480))

class RenderTextHandler(logging.Handler):
	"""A logging handler that renders warnings to a lamina panel."""	
	
	def close(self):
		"""Finalise all rendering."""
		PANEL.refresh()
	
	def emit(self, record):
		"""Render record text to the lamina panel."""
		record = self.formatter.format(record)
		_, height = FONT.size(record)
		PANEL.surf.blit(text(record), (0, 0))
		
		ogl.glPushMatrix()
		ogl.glLoadIdentity()
		ogl.glOrtho(-320, 320, -240, 240, -256, 256)
		
		ogl.glClear(ogl.GL_COLOR_BUFFER_BIT | ogl.GL_DEPTH_BUFFER_BIT)
		PANEL.refresh([pygame.Rect(0, 0, 640, height)])
		PANEL.display()
		ogl.glFlush()
		pygame.display.flip()
		
		ogl.glPopMatrix()
		
		PANEL.surf.fill((0, 0, 0, 0), (0, 0, 640, height))

CORNER = RenderTextHandler()
CORNER.setLevel(logging.DEBUG)
CORNER.setFormatter(FORMATTER)
LOAD_LOGGER.addHandler(CORNER)

def find_map(name):
	filepath = name
	if not filepath.endswith(".png"):
		filepath += ".png"
	try:
		return open(filepath, "rb")
	except IOError:
		try:
			return open(join("maps", filepath), "rb")
		except IOError, e:
			LOAD_LOGGER.error("Could not load map: %s" % filepath)
			raise e

def load_map(name):
	attributes, vertices = read(find_map(name))
	if OPTIONS.terrain:
		attributes["terrain"] = terrain.TERRAINS[OPTIONS.terrain]
	elif "terrain" not in attributes:
		attributes["terrain"] = terrain.Temperate
	else:
		attributes["terrain"] = terrain.TERRAINS[attributes["terrain"]]
	
	if OPTIONS.scale:
		attributes["scale"] = OPTIONS.scale
	elif "scale" not in attributes:
		attributes["scale"] = 8
	
	if OPTIONS.tilesize:
		attributes["tilesize"] = OPTIONS.tilesize
	elif "tilesize" not in attributes:
		attributes["tilesize"] = 16
	
	return HeightMap(vertices, tilesize=attributes["tilesize"], 
			terrain=attributes["terrain"], scale=attributes["scale"])

MAP = load_map(OPTIONS.map)
GAME = Game(MAP)
CORNER.close()

class Artillery(object):
	"""Manage the main looping of an Artillery game."""
	
	def __init__(self):
		self.clock = pygame.time.Clock()
		self.quit = False
		self.rects = []
	
	def run(self):
		"""Update and render an artillery game, passing it events as detected
		until the window is closed."""
		while not self.quit:
			self.clock.tick(50)
	
			for event in pygame.event.get():
				if event.type == pygame.QUIT:	
					self.quit = True
				else:
					GAME.event(event)
	
			GAME.update()
	
			del self.rects[:]
			ogl.glClear(ogl.GL_COLOR_BUFFER_BIT | ogl.GL_DEPTH_BUFFER_BIT)
	
			self.rects.extend(GAME.draw(PANEL))
	
			ogl.glPushAttrib(ogl.GL_DEPTH_BUFFER_BIT) # Save current depth test state
			ogl.glDisable(ogl.GL_DEPTH_TEST) # Disable depth test to draw panel on top
	
			ogl.glMatrixMode(ogl.GL_PROJECTION)
			ogl.glLoadIdentity()
			ogl.glOrtho(-320, 320, -240, 240, -256, 256)
			ogl.glMatrixMode(ogl.GL_MODELVIEW)
			ogl.glLoadIdentity()
			
			fps = text("FPS: %.1f" % self.clock.get_fps())
			PANEL.surf.blit(fps, (0, 0))
			self.rects.append(fps.get_rect())
			PANEL.refresh(self.rects)
	
			PANEL.display()
			ogl.glPopAttrib() # Restore depth test
	
			ogl.glFlush()
			pygame.display.flip()
			
Artillery().run()
